﻿using System.Collections.Generic;
using HtmlAgilityPack;

namespace MvcForum.Net.Bussiness
{

    //http://stackoverflow.com/questions/3107514/html-agility-pack-strip-tags-not-in-whitelist
    public class HtmlSanitizer
    {


        private readonly List<string> DeletableNodesXpath = new List<string>();

        private IDictionary<string, List<string>> WhiteListTags { get; set; }

        public HtmlSanitizer(IDictionary<string, List<string>> allowTags)
        {

            WhiteListTags = allowTags;

            /*  Whitelist = new Dictionary<string, string[]> {
                  { "a", new[] { "href" } },
                  { "strong", null },
                  { "em", null },
                  { "blockquote", null },
                  { "b", null},
                  { "p", null},
                  { "ul", null},
                  { "ol", null},
                  { "li", null},
                  { "div", new[] { "align" } },
                  { "strike", null},
                  { "u", null},                
                  { "sub", null},
                  { "sup", null},
                  { "table", null },
                  { "tr", null },
                  { "td", null },
                  { "th", null }
                  };*/



        }


        public string Sanitize(string input)
        {
            if (string.IsNullOrWhiteSpace(input))
                return string.Empty;

            var htmlDocument = new HtmlDocument { OptionWriteEmptyNodes = true };
            htmlDocument.LoadHtml(input);
            SanitizeNode(htmlDocument.DocumentNode);
            string xPath = CreateXPath();

            return StripHtml(htmlDocument.DocumentNode.WriteTo().Trim(), xPath);
        }

        private void SanitizeChildren(HtmlNode parentNode)
        {
            for (int i = parentNode.ChildNodes.Count - 1; i >= 0; i--)
            {
                SanitizeNode(parentNode.ChildNodes[i]);
            }
        }

        private void SanitizeNode(HtmlNode node)
        {
            if (node.NodeType == HtmlNodeType.Element)
            {
                if (!WhiteListTags.ContainsKey(node.Name))
                {
                    if (!DeletableNodesXpath.Contains(node.Name))
                    {
                        //DeletableNodesXpath.Add(node.Name.Replace("?",""));
                        node.Name = "removeableNode";
                        DeletableNodesXpath.Add(node.Name);
                    }
                    if (node.HasChildNodes)
                    {
                        SanitizeChildren(node);
                    }

                    return;
                }

                if (node.HasAttributes)
                {
                    for (int i = node.Attributes.Count - 1; i >= 0; i--)
                    {
                        HtmlAttribute currentAttribute = node.Attributes[i];
                        var allowedAttributes = WhiteListTags[node.Name];
                        if (allowedAttributes != null)
                        {
                            if (!allowedAttributes.Contains(currentAttribute.Name))
                            {
                                node.Attributes.Remove(currentAttribute);
                            }
                        }
                        else
                        {
                            node.Attributes.Remove(currentAttribute);
                        }
                    }
                }
            }

            if (node.HasChildNodes)
            {
                SanitizeChildren(node);
            }
        }

        private static string StripHtml(string html, string xPath)
        {

            if (!string.IsNullOrWhiteSpace(html))
            {
                var htmlDoc = new HtmlDocument();
                htmlDoc.LoadHtml(html);
                if (xPath.Length > 0)
                {
                    HtmlNodeCollection invalidNodes = htmlDoc.DocumentNode.SelectNodes(@xPath);
                    foreach (HtmlNode node in invalidNodes)
                    {
                        node.ParentNode.RemoveChild(node, true);
                    }
                }
                return htmlDoc.DocumentNode.WriteContentTo();
            }

            return string.Empty;

        }

        private string CreateXPath()
        {
            string _xPath = string.Empty;
            for (int i = 0; i < DeletableNodesXpath.Count; i++)
            {
                if (i != DeletableNodesXpath.Count - 1)
                {
                    _xPath += string.Format("//{0}|", DeletableNodesXpath[i]);
                }
                else _xPath += string.Format("//{0}", DeletableNodesXpath[i]);
            }
            return _xPath;
        }


    }
}